package com.example.demo.aop;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.annotation.HisDataSave;
import com.example.demo.aop.dao.AopExecuteDao;
import com.example.demo.util.EmptyUtil;
import com.example.demo.util.SpringContextUtil;
import com.example.demo.util.SqlUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@Intercepts(value = { @Signature(type = StatementHandler.class,method = "prepare"
						,args = {
								Connection.class,
								Integer.class,
						}) })
public class MybatisHisDataIntercepts implements Interceptor{
	
	private AopExecuteDao aopExecuteDao;
	
	/**
	 * mybatis-plus的baseMapper预置的删除和修改方法的方法名
	 */
	private final String MP_UPDATE_METHODS = "deleteById,deleteByMap,delete,deleteBatchIds,updateById,updateAllColumnById,update";
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
 		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		
		MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
		
		MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
		
		//sql的id(dao层的路径)
		String id = mappedStatement.getId();
		
		String sqlCommandType = mappedStatement.getSqlCommandType().toString();
		
		//如果不是update或delete类型的sql语句,不需要执行保存历史数据逻辑
		if(SqlCommandType.DELETE.toString()!=sqlCommandType && SqlCommandType.UPDATE.toString()!=sqlCommandType) {
			return invocation.proceed();
		}
		
		Configuration configuration = mappedStatement.getConfiguration();
		
		BoundSql boundSql = statementHandler.getBoundSql();
		
		Class<?> classType = Class.forName(id.substring(0, id.lastIndexOf(".")));
		
		String mName = mappedStatement.getId().substring(id.lastIndexOf(".")+1, id.length());
		
		boolean isMpMapper = BaseMapper.class.isAssignableFrom(classType);
		
		if(isMpMapper && MP_UPDATE_METHODS.contains(mName)) {
			//占位符填充完以后的sql
			String executeSql = showSql(configuration, boundSql);
			
			//保存历史数据
			saveHis(executeSql);
		} else {
			for (Method method : classType.getDeclaredMethods()) {
				if(method.isAnnotationPresent(HisDataSave.class) && mName.equals(method.getName())) {
					
					HisDataSave hisDataSave = method.getAnnotation(HisDataSave.class);
					
					if(hisDataSave.flag()) {
						
						//占位符填充完以后的sql
						String executeSql = showSql(configuration, boundSql);
						
						//保存历史数据
						saveHis(executeSql);
						
						break;
					}
				}
			}
		}
		
		Object result = invocation.proceed();
		
		return result;
	}

	@Override
	public Object plugin(Object target) {
		if(target instanceof StatementHandler) {
			return Plugin.wrap(target, this);
		}
		return target;
	}

	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
		
	}
	
	/**
	 * 
	 * @方法名 MybatisHisDataIntercepts.java
	 * @创建者 xzw
	 * @创建日期 2021年10月15日
	 * @返回类型 String
	 * @功能描述 展示处理后的sql语句
	 *
	 */
	private static String showSql(Configuration configuration,BoundSql boundSql) {
		String oldSql = boundSql.getSql();
		
		String retSql = oldSql.replaceAll("\n", " ").replaceAll("[\\s]+", " ");//去掉sql语句的所有换行，且将多个空格转换为一个空格
		
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		
        Object parameterObject = boundSql.getParameterObject();
		
		if(EmptyUtil.isNotEmpty(parameterMappings)&&null!=parameterObject) {
			
			TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
			
			if(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
				retSql = retSql.replaceFirst("\\?", getParameterValue(parameterObject));
			} else {
				
				MetaObject metaObject = configuration.newMetaObject(parameterObject);
				
				for (ParameterMapping parameterMapping : parameterMappings) {
					String propertyName = parameterMapping.getProperty();
					
					if(metaObject.hasGetter(propertyName)) {
						Object obj = metaObject.getValue(propertyName);
						
						retSql = retSql.replaceFirst("\\?", getParameterValue(obj));
						
					} else if (boundSql.hasAdditionalParameter(propertyName)) {
						Object obj = boundSql.getAdditionalParameter(propertyName);
						retSql = retSql.replaceFirst("\\?", getParameterValue(obj));
					} else {
						retSql = retSql.replaceFirst("\\?", "缺失");
					}
				}
			}
		}
		
		
		return retSql;
	}
	
	/**
	 * 
	 * @方法名 MybatisHisDataIntercepts.java
	 * @创建者 xzw
	 * @创建日期 2021年10月15日
	 * @返回类型 String
	 * @功能描述 处理参数值
	 *
	 */
	private static String getParameterValue(Object obj) {
		String value = null;
		
		if (obj instanceof String) {
			value = "'" + obj.toString() + "'";
		} else if (obj instanceof Date) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			return "'" + sdf.format((Date)(obj)) + "'";
		} else {
			if(null != obj) {
				value = obj.toString();
			} else {
				value = "";
			}
		}
		
		return value;
	}
	
	/**
	 * 
	 * @方法名 MybatisHisDataIntercepts.java
	 * @创建者 xzw
	 * @创建日期 2021年10月20日
	 * @返回类型 void
	 * @功能描述 保存历史数据
	 *
	 */
	private void saveHis(String oldSql) {
		String hisSql = null;
		
		try {
			hisSql = SqlUtils.getInsertHisSql(oldSql);
		} catch (Exception e) {
			log.error("获取保存历史记录sql失败",e);
			return;
		}
		
		if(EmptyUtil.isEmpty(hisSql)) {
			return;
		}
		
		if(null==aopExecuteDao) {
			aopExecuteDao = SpringContextUtil.getBean(AopExecuteDao.class);
		}
		
		try {
			aopExecuteDao.update(hisSql);
		} catch (BadSqlGrammarException e) {
			log.error("没有预先创建历史数据记录表,无法自动记录历史数据");
			return;
		} catch (Exception e) {
			log.error("保存历史记录失败,未知的异常,请联系管理员");
			return;
		}
	}
}
